package com.wang.jmonkey.gateway.handler.auth.authorization;

import cn.hutool.core.collection.CollUtil;
import com.wang.jmonkey.common.constant.CacheConstant;
import com.wang.jmonkey.common.model.enums.BooleanEnum;
import com.wang.jmonkey.common.model.pojo.LoginUser;
import com.wang.jmonkey.common.model.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/**
 * @Description 授权管理
 * @Author HeJiawang
 * @Date 2021/5/31 13:12
 */
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Autowired
    private RedisTemplate<String , Object> redis;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        ServerHttpRequest request = exchange.getRequest();

        return authentication.map(auth -> {
            LoginUser user = (LoginUser)auth.getPrincipal();
            if (user.getIsAdmin().compareTo(BooleanEnum.YES) == 0) {
                return new AuthorizationDecision(true);
            }

            String userName = user.getUsername();
            Map<String, List<String>> permissionMap = redis.execute(new SessionCallback<>(){

                @Override
                public Map<String, List<String>> execute(RedisOperations operations) throws DataAccessException {
                    if (operations.opsForHash().hasKey(CacheConstant.PermissionCache.USER_PERMISSION.getKey(), userName)) {
                        return (Map) operations.opsForHash().get(CacheConstant.PermissionCache.USER_PERMISSION.getKey(), userName);
                    }

                    return null;
                }
            });

            if (CollUtil.isEmpty(permissionMap) ||
                    CollUtil.isEmpty(permissionMap.get(request.getMethod().name().toLowerCase()))
            ) return new AuthorizationDecision(false);

            List<String> urlList = permissionMap.get(request.getMethod().name().toLowerCase());
            for ( String url : urlList ) {
                if (antPathMatcher.match(url, request.getURI().getPath())) {
                    return new AuthorizationDecision(true);
                }
            }

            return new AuthorizationDecision(false);
        }).defaultIfEmpty(new AuthorizationDecision(false));
    }
}
